package moodish.server;

import java.util.HashMap;
import java.util.LinkedList;
import moodish.client.*;
import javax.swing.JOptionPane;
import moodish.client.MoodishClient;
import moodish.comm.ServerComm;
import moodish.comm.ServerSideMessage;

/*REQUIREMENT: The server must implement the MoodishServer interface.*/
public class Server implements MoodishServer {

	private LinkedList<Client> onlineClients;
	
	/*The server must keep track of who is friend of whom and forward Moodish moods accordingly*/
	private HashMap<String, LinkedList<Client>> AllfriendshipLists;
	
	/*The server must keep clients informed about who their friends are and who decided to unfriend*/
	private LinkedList<Client> friendshipList;
	
	private ServerSideMessage msg;
	
	private MoodishClient newMoodishClient;
	
	private int num_Erros=0;
	
	
	/*REQUIREMENT: The server must use the ServerComm interface to facilitate communication 
	 * Moodish clients.
	 *REQUIREMENT: A client can only be connected to one server at the time.*/
	@Override
	public void start(ServerComm serverComm) {
		serverComm.start();
		
		while (true) {
			msg = serverComm.getNextMessage();
						
			if (msg.getType() == ServerSideMessage.Type.CLIENT_CONNECTED) {
				serverResponseToClientConnectedMessage(serverComm);
			
			} else if (msg.getType() == ServerSideMessage.Type.CLIENT_DISCONNECTED) {
				serverResponseToClientDisconnectedMessage(serverComm);

			} else if (msg.getType() == ServerSideMessage.Type.FRIENDSHIP) {
				serverResponseToFriendship(serverComm);

			} else if (msg.getType() == ServerSideMessage.Type.UNFREINDSHIP) {
				serverResponseToUnfriendship(serverComm);

			} else if (msg.getType() == ServerSideMessage.Type.MOODISH_MESSAGE) {
				serverResponseToMoodishMessage(serverComm);

			}
		}
	}

	/*CONNECT TO SERVER: Connect to a Moodish server with a certain nickname (username). 
	
	 *REQUIREMENT: When a new client connects, the server send the list of currently connected friends by mood 
	 *to that client so that the client will have an updated list of all currently connected friends.
	 * REQUIREMENT: When a client connects or disconnects, the server should a message with the 
	 * appropriate information to all connected clients.*/
	private void serverResponseToClientConnectedMessage(ServerComm serverComm) {
		if (serverComm.clientIsConnected(msg.getClientNickname())) {
			Client client= new Client(msg.getClientNickname());
			onlineClients.add(client);
			
			for (Client c : onlineClients) {
				//OnlineClients will show all the other Clients's Friends that are also connected
				if (!c.getClientNickname().equals(client.getClientNickname())) {
					//Sends a notification to the the Client 
					serverComm.sendClientConnected(client.getClientNickname(), c.getClientNickname());	
					//Sends a notification to the Client's Friend
					serverComm.sendClientConnected(c.getClientNickname(), client.getClientNickname());		
				}
			}
		}else {
			setNum_Erros(getNum_Erros() + 1);
			serverComm.sendError(msg.getClientNickname(), "Something went wrong when trying to connect to the server!");
		}
	}
	
	/*DISCONNECT: Disconnect from a server.
	 * REQUIREMENT: When a client connects or disconnects, the server should a message with the 
	 * appropriate information to all connected clients.*/
	private void serverResponseToClientDisconnectedMessage(ServerComm serverComm) {
		for (Client c : onlineClients) {
			if (c.getClientNickname().equals(msg.getClientNickname())) {
				onlineClients.remove(c.getClientNickname());
				//The Client is removed from the list of onlineClients and disconected from the server
				serverComm.disconnectClient(msg.getClientNickname());
				JOptionPane.showMessageDialog(null, "User removed successfully!");
				break;
			}else {
				setNum_Erros(getNum_Erros() + 1);
				serverComm.sendError(c.getClientNickname(), "Problem with disconection from "+c.getClientNickname());
			}
		}
		
		for (Client c : onlineClients) {
			//All Client's Friends recieve notification of the disconnection
			serverComm.sendClientDisconnected(c.getClientNickname(), msg.getClientNickname());
		}
	}
	
	/*FRIENDSHIP: To be friend of a specific user*/
	private void serverResponseToFriendship(ServerComm serverComm) {
		for (Client c : onlineClients) {
			if (c.getClientNickname().equals(msg.getClientNickname())) {
				 //Server sends to the user the request
				serverComm.sendNewFriendship(c.getClientNickname(), msg.getClientNickname());
				//The user is added to the Client's friendshipList 
				friendshipList.add(c);
				AllfriendshipLists.put(c.getClientNickname(), friendshipList);
				if (AllfriendshipLists.get(msg.getPayload()).equals(null)) {
					setNum_Erros(getNum_Erros() + 1);
					serverComm.sendError(c.getClientNickname(), "Message Null!!");
				}
			}
		}
	}
	
	/*UNFRIENDSHIP: Stop being friend of a specific user. */
	/*REQUIREMENT:The server must keep clients informed about who their friends are and 
	 * who decided to unfriend*/
	private void serverResponseToUnfriendship(ServerComm serverComm) {
		for (Client c : onlineClients) {
			if (c.getClientNickname().equals(msg.getClientNickname())) {
				//Client sends notification of the decision to unfriend
				serverComm.sendNewUnfriendship(c.getClientNickname(), msg.getClientNickname());
				String c1= "";
				c1= AllfriendshipLists.containsValue(msg.getClientNickname())+"";
				//Removes Client's Friend nickname from his friendshisps list
				AllfriendshipLists.remove(c1);
				if (AllfriendshipLists.get(msg.getPayload()).equals(null)) {
					setNum_Erros(getNum_Erros() + 1);
					serverComm.sendError(c.getClientNickname(), "Message Null!!");
				}
			}
		}
	}
	
	/*RECIEVE MESSAGE FROM FRIENDS: When a user currently our friend sends a message to the
	 * server, the message should be relayed to the client and displayed in the client UI.*/
	private void serverResponseToMoodishMessage(ServerComm serverComm) {
		// Answering to another Client message 
		for (Client client : onlineClients) {
			if (client.getClientNickname().equals(msg.getClientNickname())) {
				serverComm.sendMoodishMessage(client.getClientNickname(),
						msg.getClientNickname(), msg.getPayload());
			}else{
				setNum_Erros(getNum_Erros() + 1);
				serverComm.sendError(client.getClientNickname(), "Client not found!");
			}

		}
	}
	

	public int getNum_Erros() {
		return num_Erros;
	}

	public void setNum_Erros(int num_Erros) {
		this.num_Erros = num_Erros;
	}

	
	public MoodishClient getNewMoodishClient() {
		return newMoodishClient;
	}

	public void setNewMoodishClient(MoodishClient newMoodishClient) {
		this.newMoodishClient = newMoodishClient;
	}


	class Client{
		private String nickName;

		public Client(String nickName) {
			this.nickName = nickName;
		}

		public String getClientNickname() {
			return nickName;
		}
		public boolean onOff(){
			return false;
		}
	}
	
}
